package bean_map_plugin

import (
	"fmt"
	"gitee.com/kristas/booting-go/framework/common/util/list"
	"gitee.com/kristas/booting-go/framework/common/util/reflectx"
	"gitee.com/kristas/booting-go/framework/core/bean"
	"gitee.com/kristas/booting-go/framework/core/ioc/bean_factory"
	"gitee.com/kristas/booting-go/framework/core/statement/types"
	"gitee.com/kristas/booting-go/gen_tool/booctl/util"
	"log"
	"os"
	"reflect"
	"sort"
)

type BeanMapPlugin struct {
	types.ApplicationRunner `order:"999"`
	Beans                   []bean.Bean `wire:""`
}

func New() *BeanMapPlugin {
	return &BeanMapPlugin{}
}

func (b *BeanMapPlugin) Run() error {
	sort.Slice(b.Beans, func(i, j int) bool {
		return bean.GetBeanName(b.Beans[i]) < bean.GetBeanName(b.Beans[j])
	})
	write("beans:\n")
	for i, b := range b.Beans {
		walkBean(b, i+1)
	}
	file.Close()
	log.Fatal("finish", len(b.Beans))
	return nil
}

func walkBean(b interface{}, id int) {
	name := bean.GetName(b)
	if sets.Exists(name) {
		return
	}
	write(`  - %d.%s: "%s"`+"\n", id, name, reflect.TypeOf(b).String())
	sets.Put(name)
	reflectx.ForEachField(b, true, func(field reflect.StructField, value reflect.Value) error {
		if _, ok := field.Tag.Lookup(bean_factory.InjectFieldTag); !ok {
			return nil
		}
		switch field.Type.Kind() {
		case reflect.Slice:
			for i := 0; i < value.Len(); i++ {
				walkBean(value.Index(i).Interface(), id)
			}
		default:
			walkBean(value.Interface(), id)
		}
		return nil
	})
}

var file *os.File
var sets = list.NewConcurrentSets("bean_map_plugin.BeanMapPlugin")

func init() {
	var err error
	os.Remove("resources/bean_map.yaml")
	file, _, err = util.OpenOrCreateFile("resources/bean_map.yaml", os.O_RDWR)
	if err != nil {
		panic(err)
	}
	fmt.Println(file.Name())
}

func write(format string, a ...interface{}) {
	_, err := file.WriteString(fmt.Sprintf(format, a...))
	if err != nil {
		panic(err)
	}
}
